package com.fastCms.business.frame.busi.impl;

import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.RandomUtil;
import com.fastCms.business.frame.config.UnifyVerifyConfig;
import com.fastCms.business.frame.constant.RedisConst;
import com.fastCms.business.frame.constant.ResultCodeConst;
import com.fastCms.business.frame.enums.ValidateCodeSceneEnum;
import com.fastCms.business.frame.exception.BusinessException;
import com.fastCms.business.frame.busi.ValidateManageService;
import com.fastCms.business.frame.plugins.email.EmailProperties;
import com.fastCms.business.frame.plugins.email.EmailUtil;
import com.fastCms.business.frame.plugins.sms.SmsManager;
import com.fastCms.business.frame.pojo.po.ValidateCodeCheckPO;
import com.fastCms.business.frame.pojo.po.ValidateCodeSendPO;
import com.fastCms.business.frame.pojo.po.ValidateEmailSendPO;
import com.fastCms.business.frame.response.ApiResponse;
import com.fastCms.business.frame.response.ResponseUtil;
import com.fastCms.business.frame.utils.PatternUtil;
import com.fastCms.business.frame.utils.RedisUtil;
import com.fastCms.business.frame.utils.VerifyCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Base64;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 哈密瓜科技 版权所有 © Copyright 2019<br>
 *
 * @Description: 登录权限控制<br>
 * @Project: hami <br>
 * @CreateDate: Created in 2019/3/7 下午3:48 <br>
 * @Author: <a href="lsquan666@163.com">lsquan</a>
 */
@Transactional(rollbackFor = Exception.class)
@Component
@Slf4j
public class ValidateManageServiceImpl implements ValidateManageService {

    // 验证码图片的宽度。
    private int width = 100;
    // 验证码图片的高度。
    private int height = 40;
    // 验证码字符个数
    private int codeCount = 4;

    @Resource
    private UnifyVerifyConfig unifyVerifyConfig;

    @Resource
    private EmailProperties emailProperties;

    /**
     * 图像验证码
     *
     * @param response
     * @param request
     * @return
     * @throws IOException
     */
    @Override
    public String getPicValidateCode(HttpServletResponse response, HttpServletRequest request) throws IOException {
        // 禁止图像缓存。
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setContentType("image/jpeg");
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        String verifyCode = VerifyCodeUtils.generateVerifyCode(codeCount);
        ByteArrayOutputStream os = VerifyCodeUtils.outputImage(width, height, outputStream, verifyCode);

        RedisUtil.getRedisTemplate().boundValueOps(RedisConst.VALIDATE.PIC_NUM_CODE + verifyCode).set(verifyCode, RedisConst.VALIDATE.VALID_CODE_TIME, TimeUnit.SECONDS);

        byte[] bytes = os.toByteArray();
        String base64 = Base64.getEncoder().encodeToString(bytes).trim();
        return base64;
    }


    @Override
    public ApiResponse<Boolean> checkPicValidateCode(HttpServletRequest request, String validCode) {
        if (StringUtils.isBlank(validCode)) {
            return ResponseUtil.fail(ResultCodeConst.VALIDATE_CODE.NOTNULL, ResultCodeConst.VALIDATE_CODE.NOTNULL_MSG);
        }

        String rgValidCode = RedisUtil.getRedisTemplate().boundValueOps(RedisConst.VALIDATE.PIC_NUM_CODE + validCode).get();

        if (StringUtils.isBlank(rgValidCode)) {
            return ResponseUtil.fail(ResultCodeConst.VALIDATE_CODE.EXPIRE, ResultCodeConst.VALIDATE_CODE.EXPIRE_MSG);
        }

        if (!validCode.equalsIgnoreCase(rgValidCode)) {
            return ResponseUtil.fail(ResultCodeConst.VALIDATE_CODE.ERROR, ResultCodeConst.VALIDATE_CODE.ERROR_MSG);
        } else {
            //验证成功，删除存储的验证码
            RedisUtil.getRedisTemplate().delete(RedisConst.VALIDATE.PIC_NUM_CODE + validCode);
            return ResponseUtil.success(true);
        }
    }

    @Override
    public ApiResponse<Boolean> smsSendValidateCode(ValidateCodeSendPO param) {
        Integer SEND_NUM_LIMIT = null != unifyVerifyConfig && Objects.nonNull(unifyVerifyConfig.getLimitNum()) ? unifyVerifyConfig.getLimitNum() : 5;     //发送次数限制
        Integer EXPIRE_MINUTE = null != unifyVerifyConfig && Objects.nonNull(unifyVerifyConfig.getExpireMinutes()) ? unifyVerifyConfig.getExpireMinutes() : 15;     //发送次数限制
        Integer LIMIT_PERIOD = null != unifyVerifyConfig && Objects.nonNull(unifyVerifyConfig.getLimitPeriod()) ? unifyVerifyConfig.getLimitPeriod() : 1;     //发送次数限制
        String SMS_TEMPLATE_CODE = "11434";    //短信验证码
        String phone = param.getPhone();
        //2.手机号有效性校验
        if (StringUtils.isBlank(phone) || !PhoneUtil.isMobile(phone)) {
            return ResponseUtil.fail("手机号码不正确");
        }

        //3.手机号发送次数限制(phoneRedisKey结构 : PHONE_SEND_NUM_1:157575851174 ,
        ValidateCodeSceneEnum businessSceneEnum = ValidateCodeSceneEnum.getEnum(param.getScene());
        String phoneSendNum = MessageFormat.format(RedisConst.VALIDATE.PHONE_SEND_NUM, businessSceneEnum.getType()) + phone;
        StringRedisTemplate redisTemplate = RedisUtil.getRedisTemplate();
        String numStr = redisTemplate.boundValueOps(phoneSendNum).get();
        if (numStr != null && Integer.parseInt(numStr.toString()) >= SEND_NUM_LIMIT) {
            // 超过限制次数，不生成验证码，直接返回
            return ResponseUtil.fail("验证码已失效");
        }

        //4.生成验证码,并保存在redis中(redisKey:phone_code_1:15757851174）
        String yzm = RandomUtil.randomNumbers(6);
        String redisKey = MessageFormat.format(RedisConst.VALIDATE.PHONE_CODE, businessSceneEnum.getType()) + phone;

        //5.开关打开->调用发送短信
        if (unifyVerifyConfig.getIsSendMessage()) {
            //5.1发送短信
            SmsManager.getHandler("ALIYUN").send(SMS_TEMPLATE_CODE, phone, yzm);

            //5.2 保存发送记录
//            FacedeSendMsg sendMsg = new FacedeSendMsg();
//            sendMsg.setOutMsgId(msgId);
//            sendMsg.setBusinessCode(businessSceneEnum.getCode());
//            sendMsg.setMsgType(VerifyTypeEnum.PHONE.getKey());
//            sendMsg.setReceiver(phone);
//            sendMsg.setTemplateCode(String.valueOf(smsTemplateCode));
//            sendMsg.setContent(yzm);
//            userServiceSpi.insertMsg(sendMsg);
        }

        //6.保存验证码信息
        RedisUtil.save(redisKey, yzm, EXPIRE_MINUTE, TimeUnit.MINUTES);

        //7.次数限制统计
        if (numStr == null) {
            // 7.1初始化统计次数
            String INIT_NUM = "1";
            RedisUtil.save(phoneSendNum, INIT_NUM, EXPIRE_MINUTE, TimeUnit.MINUTES);
        } else if (Integer.parseInt(numStr.toString()) <= SEND_NUM_LIMIT) {
            // 7.2 累加
            RedisUtil.increment(phoneSendNum);
        }
        return ResponseUtil.success(true);
    }


    @Override
    public ApiResponse<Boolean> sendEmailCode(ValidateEmailSendPO param) throws BusinessException {
        Integer SEND_NUM_LIMIT = null != unifyVerifyConfig && Objects.nonNull(unifyVerifyConfig.getLimitNum()) ? unifyVerifyConfig.getLimitNum() : 5;     //发送次数限制
        Integer EXPIRE_MINUTE = null != unifyVerifyConfig && Objects.nonNull(unifyVerifyConfig.getExpireMinutes()) ? unifyVerifyConfig.getExpireMinutes() : 15;     //发送次数限制
        Integer LIMIT_PERIOD = null != unifyVerifyConfig && Objects.nonNull(unifyVerifyConfig.getLimitPeriod()) ? unifyVerifyConfig.getLimitPeriod() : 1;     //发送次数限制

//        if (isCommonOnline && !TicketAuthUtil.isCaptchaVerification(ticket)) {
//            throw new UnifyException("token校验失败");
//        }
        String email = param.getEmail();
        if (StringUtils.isBlank(email) || !PatternUtil.isEmail(email)) {
            return ResponseUtil.fail("验证码已失效");
        }
        ValidateCodeSceneEnum businessSceneEnum = ValidateCodeSceneEnum.getEnum(param.getScene());

        String emailSendNum = MessageFormat.format(RedisConst.VALIDATE.EMAIL_CODE, businessSceneEnum.getType()) + email;
        Object numStr = RedisUtil.get(emailSendNum);
        if (numStr != null && Integer.parseInt(numStr.toString()) >= SEND_NUM_LIMIT) {
            // 超过限制次数，不生成验证码，直接返回
            return ResponseUtil.fail("超过发送上限,请稍后再试");
        }

        String yzm = RandomUtil.randomNumbers(6);
        String keyEmail = MessageFormat.format(RedisConst.VALIDATE.EMAIL_CODE, businessSceneEnum.getType()) + email;
        if (unifyVerifyConfig.getIsSendMessage()) {
            new EmailUtil(emailProperties).sendMail(email, yzm, EXPIRE_MINUTE);
//            //5.2 保存发送记录
//            FacedeSendMsg sendMsg = new FacedeSendMsg();
//            sendMsg.setMsgType(VerifyTypeEnum.EMAIL.getKey());
//            sendMsg.setReceiver(email);
//            sendMsg.setTemplateCode(String.valueOf(smsTemplateCode));
//            sendMsg.setContent(yzm);
//            sendMsg.setBusinessCode(businessSceneEnum.getCode());
//            userServiceSpi.insertMsg(sendMsg);
        }
        RedisUtil.save(keyEmail, yzm, EXPIRE_MINUTE, TimeUnit.MINUTES);

        if (numStr == null) {
            // 7.1初始化统计次数
            String INIT_NUM = "1";
            RedisUtil.save(emailSendNum, INIT_NUM, LIMIT_PERIOD, TimeUnit.HOURS);
        } else if (Integer.parseInt(numStr.toString()) <= SEND_NUM_LIMIT) {
            // 没有超过限制次数
            RedisUtil.increment(emailSendNum);
        }
        return ResponseUtil.success(true);
    }

    @Override
    public ApiResponse<Boolean> checkSmsValidateCode(ValidateCodeCheckPO param) {
        Integer scene = param.getScene();
        String validateCode = param.getValidateCode();
        if (StringUtils.isBlank(validateCode)) {
            return ResponseUtil.fail(ResultCodeConst.VALIDATE_CODE.NOTNULL, ResultCodeConst.VALIDATE_CODE.NOTNULL_MSG);
        }
        String smsRedisKey = scene + "_" + RedisConst.VALIDATE.EMAIL_CODE + validateCode;
        String rgValidCode = RedisUtil.getRedisTemplate().boundValueOps(smsRedisKey).get();
        if (StringUtils.isBlank(rgValidCode)) {
            return ResponseUtil.fail(ResultCodeConst.VALIDATE_CODE.EXPIRE, ResultCodeConst.VALIDATE_CODE.EXPIRE_MSG);
        }

        if (!validateCode.equalsIgnoreCase(rgValidCode)) {
            return ResponseUtil.fail(ResultCodeConst.VALIDATE_CODE.ERROR, ResultCodeConst.VALIDATE_CODE.ERROR_MSG);
        } else {
            //验证成功，删除存储的验证码
            RedisUtil.getRedisTemplate().delete(smsRedisKey);
            return ResponseUtil.success(true);
        }
    }

    @Override
    public ApiResponse<Boolean> checkEmailCode(ValidateEmailSendPO param) {
        return null;
    }

}
